<?php

class blogs extends Module {
	
	private
		$currentBlogId      = 0,
		$aCurrentErrors     = array (),
		$aCurrentValues     = array (),
		$aUserBlogs         = null,
		$aUserModerateBlogs = null,
		$aUserAdminBlogs    = null,
		$aListFilters       = array (),
		$aListAttributes    = array ();
		
	private static
		$aPostsTypes = null;
	
	public function __construct () {
		parent::__construct(__CLASS__);
		
		$this->extend ('__blogs_ajax');
		$this->extend ('__blogs_mail');
		$this->extend ('__custom_blogs');
	}
	
	public function __default () {
		return $this->getPostsList (
			null,
			array (
				'draft' => 0,
				'rating' => array ((int) Regedit::getInstance()->get('//modules/blogs/rating_to_mainpage'), '>=')
			),
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => '',
				'user-id' => 0
			)
		);
	}
	
	/**
	 * Получить весь список публикаций
	 * @return ModuleResponse
	 */
	public function all () {
		$this->setPageTitle (lang('header_all',__CLASS__));
		
		return $this->getPostsList (
			null,
			array (
				'draft' => 0
			),
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => '',
				'user-id' => 0
			)
		);
	}
	
	/**
	 * Получить список тематических публикаций
	 * @return ModuleResponse
	 */
	public function thematic () {
		$this->setPageTitle (lang('header_thematic',__CLASS__));
		
		return $this->getPostsList (
			null,
			array (
				'draft' => 0,
				'blog_id' => array (0, '!=')
			),
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => '',
				'user-id' => 0
			)
		);
	}
	
	/**
	 * Получить список персональных публикаций
	 * @return ModuleResponse
	 */
	public function personal () {
		$this->setPageTitle (lang('header_personal',__CLASS__));
		
		return $this->getPostsList (
			null,
			array (
				'draft' => 0,
				'blog_id' => 0
			),
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => '',
				'user-id' => 0
			)
		);
	}
	
	/**
	 * Получить список блогов
	 * @return ModuleResponse
	 */
	public function top () {
		$oTypeBlog = TypesController::getInstance()->getTypeByName ('blog');
		$objectsController = ObjectsController::getInstance ();
		
		$this->setPageTitle (lang('header_blogs_top',__CLASS__));
		
		$sel = new Selection;
		$sel->addType ($oTypeBlog);
		$sel->setOrder ('rating', false);
		
		$currentPage = getRequest ('page', 'int');
		if ($currentPage > 0) {
			$currentPage--;
		}
		$onPage = (int) Regedit::getInstance()->get ('//modules/blogs/blogs_on_page');
		if ($onPage == 0) {
			$onPage = 15;
		}
		
		$sel->addLimit ($currentPage*$onPage, $onPage);
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
		
		$response = new ModuleResponse;
		$response->setTemplate ('blogs/top');
		
		foreach ($aObjects as $object) {
			$rating = $object->getValue ('rating');
			$object->setValue ('rating', number_format ($rating, 2, ',', ' '));
			
			$response->addObject ($object);
		}
		
		$response->addObject (new Array2XML ('pages', array (
			'onpage' => $onPage,
			'total' => $sel->getTotalCount (),
			'page' => ++$currentPage
		)));
		
		return $response;
	}
	
	/**
	 * Получить список публикаций пользователя
	 * @return ModuleResponse
	 */
	public function user () {
		$userLogin = getRequest ('param0');
		if ($userLogin == '') {
			throw new NotFound;
		}
		
		$oUserType = TypesController::getInstance()->getTypeByName ('user');
		
		$sel = new Selection;
		$sel->addType ($oUserType);
		$sel->addFilter ('login', $userLogin);
		$sel->addLimit (1);
		
		$res = $sel->getList ();
		
		if (count ($res) == 0) {
			throw new NotFound;
		}
		
		$oUser = ObjectsController::getInstance()->getObject ($oUserType, $res[0][0]);
		$userName = $oUser->getValue ('name');
		$this->setPageTitle (lang('user_blog',__CLASS__) . $userName);
		
		$aFilters = array ('owner_id' => $res[0][0]);
		
		if ($res[0][0] != ExplaySession::getUserId ()) {
			$aFilters['draft'] = 0;
		}
		
		return $this->getPostsList (
			null,
			$aFilters,
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => $userLogin,
				'user-id' => $res[0][0]
			)
		);
	}
	
	/**
	 * Получить список публикаций блога
	 * @return ModuleResponse
	 */
	public function blog () {
		if (mb_strlen ($blogName = getRequest ('param0'))) {
			$oBlogType = TypesController::getInstance()->getTypeByName ('blog');
			
			$sel = new Selection;
			$sel->addType ($oBlogType);
			$sel->addFilter ('alias', $blogName);
			$sel->addLimit (1);
			
			$blog = $sel->getList ();
			
			if (count ($blog) == 0) {
				throw new NotFound;
			}
			
			$oBlog = ObjectsController::getInstance()->getObject ($oBlogType, $blog[0][0]);
			$this->setPageTitle ($oBlog->getValue ('title') . lang('header_blog',__CLASS__));
			
			$this->currentBlogId = $blog[0][0];
			
			return $this->getPostsList (
				null,
				array (
					'blog_id' => $blog[0][0],
					'draft' => 0
				),
				null,
				array (
					'blog-id' => $blog[0][0],
					'blog-name' => $blogName,
					'user-login' => '',
					'user-id' => 0
				)
			);
		}

		return $this->__default ();
	}
	
	/**
	 * Вовод публикации
	 * @return ModuleResponse
	 */
	public function post () {
		if (!mb_strlen ($sTypeName = getRequest ('param0'))) {
			throw new NotFound;
		}
		if (!mb_strlen ($id = getRequest ('param1', 'int'))) {
			throw new NotFound;
		}
		
		if (!$oType = TypesController::getInstance()->getTypeByName ($sTypeName)) {
			throw new NotFound;
		}
		
		if (!self::validateType ($oType->getId ())) {
			throw new NotFound;
		}
		
		if (!$object = ObjectsController::getInstance()->getObject ($oType, $id)) {
			throw new NotFound;
		}
		
		$object->setAttribute ('editable', $this->isPostEditable ($object->getValue ('owner_id'), $object->getValue ('blog_id')) ? '1' : '0');
		
		if ($title = $object->getValue ('title')) {
			CMSController::setPageTitle ($title);
		}
		
		$text = $object->getValue ('text');
		$object->setValue ('text', $text);
		
		$response = new ModuleResponse;
		$response->addObject ($object);
		if ($object->getValue ('owner_id') != ExplaySession::getUserId() && userGroup (ExplaySession::getUser()->getValue ('group')) != 'guest') {
			$object->setAttribute ('vote-allow', '1');
		} else {
			$object->setAttribute ('vote-allow', '0');
		}
		$response->setTemplate ('blogs/post');
		return $response;
	}
	
	/**
	 * Фильтрация по тегу через ЧПУ
	 * @return ModuleResponse
	 */
	public function tag () {
		if (getRequest ('param0') == 'go' && getRequest ('tag')) {
			redirect (HTTP_HOST . '/blogs/tag/' . safeURI (getRequest ('tag')));
		}
		
		return $this->getPostsList (
			null,
			array (
				'tags' => getRequest ('param0'),
				'draft' => 0
			),
			null,
			array (
				'blog-id' => 0,
				'blog-name' => '',
				'user-login' => '',
				'user-id' => 0,
				'tag-filter' => getRequest ('param0')
			)
		);
	}
	
	/**
	 * Переход по ссылке
	 * @return void
	 */
	public function link () {
		if ($oLinkType = TypesController::getInstance()->getTypeByName ('link')) {
			$objectId = getRequest ('param0', 'int');

			if (!$oLink = ObjectsController::getInstance()->getObject ($oLinkType, $objectId)) {
				throw new NotFound;
			}
			
			$uri = $oLink->getValue ('link');
			
			$clicks = (int) $oLink->getValue ('clicks');
			$oLink->setValue ('clicks', ++$clicks);
			$oLink->save ();
			
			redirect ($uri);
		}
	}
	
	/**
	 * Добавить публикацию
	 * @return ModuleResponse
	 */
	public function add () {
		$this->setPageTitle (lang('title_post_add',__CLASS__));
		
		if (mb_strlen (getRequest ('param0')) == 0) {
			$response = new ModuleResponse;
			$response->setTemplate ('blogs/addPostMain');
			return $response;
		}
		
		$minRating = (float) Regedit::getInstance()->get ('//modules/blogs/min_rating_post');
		$userRating = (float) ExplaySession::getUser()->getValue ('rating');
		
		if ($userRating < $minRating) {
			$response = new ModuleResponse;
			$response->addObject (new SystemError (lang ('error_rating_post_add',__CLASS__)));
			$response->setTemplate ('index');
			return $response;
		}
		
		if (getRequest ('param1') == 'save') {
			$request = $this->savePost ();
			if ($request !== false) {
				return $request;
			}
		}
		return $this->postEditForm (getRequest ('param0'));
	}
	
	/**
	 * Редактировать публикацию
	 * @return ModuleResponse
	 */
	public function edit () {
		$this->setPageTitle (lang('title_post_edit',__CLASS__));
		
		if (getRequest ('param2') == 'save') {
			$request = $this->savePost ();
			if ($request !== false) {
				return $request;
			}
		}
		return $this->postEditForm (getRequest ('param0'), getRequest ('param1', 'int'));
	}
	
	/**
	 * Удалить публикацию
	 * @return ModuleResponse
	 */
	public function delete () {
		throw new NotFound;
		
		$typesController = TypesController::getInstance();
		$objectsController = ObjectsController::getInstance();
		
		$requestType = getRequest ('param0');
		if (!$oType = $typesController->getTypeByName ($requestType)) {
			throw new NotFound;
		}
		if (!$this->validateType ($oType->getId ())) {
			throw new NotFound;
		}
		
		if (!$object = $objectsController->getObject ($oType, getRequest ('param1'))) {
			throw new NotFound;
		}
		
		$oBlog = $objectsController->getObject ($typesController->getTypeByName ('blog'), $object->getValue ('blog_id'));
		
		$object->delete ();
		
		if ($oBlog) {
			$count = $oBlog->getValue ('count_posts');
			$oBlog->setValue ('count_posts', --$count);
			$oBlog->save ();
		}
		
		$response = new ModuleResponse;
		$response->setTemplate ('blogs/deletePost');
		return $response;
	}
	
	/**
	 * Создать блог
	 * @return ModuleResponse
	 */
	public function create_blog () {
		$this->setPageTitle (lang('title_blog_add',__CLASS__));
		
		$minRating = (float) Regedit::getInstance()->get ('//modules/blogs/min_rating_create_blog');
		$userRating = (float) ExplaySession::getUser()->getValue ('rating');
		
		if ($userRating < $minRating) {
			$response = new ModuleResponse;
			$response->addObject (new SystemError (lang ('error_rating_create_blog',__CLASS__)));
			$response->setTemplate ('index');
			return $response;
		}
		
		if (getRequest ('param0') == 'save') {
			$request = $this->saveBlog ();
			if ($request !== false) {
				return $request;
			}
		}
		return $this->blogEditForm ();
	}
	
	/**
	 * Редактировать блог
	 * @return ModuleResponse
	 */
	public function edit_blog () {
		$this->setPageTitle (lang('title_blog_edit',__CLASS__));
		
		if (getRequest ('param1') == 'save') {
			$request = $this->saveBlog ();
			if ($request !== false) {
				return $request;
			}
		}
		return $this->blogEditForm (getRequest ('param0', 'int'));
	}
	
	/**
	 * Редактировать участников блога
	 * @return ModuleResponse
	 */
	public function edit_blog_members () {
		$typesController = TypesController::getInstance ();
		$objectsController = ObjectsController::getInstance ();
		
		$oTypeMember = $typesController->getTypeByName ('blog_member');
		$oTypeAdmin = $typesController->getTypeByName ('blog_admin');
		$oTypeModerator = $typesController->getTypeByName ('blog_moderator');
		
		$blogId = getRequest ('param0', 'int');
		
		// 403
		if (!$this->isUserAdminBlog($blogId)) {
			throw new AccessException (lang('error_edit_blog_not_admin',__CLASS__));
		}
		
		$sel = new Selection;
		$sel->addType ($oTypeMember);
		$sel->addType ($oTypeAdmin);
		$sel->addType ($oTypeModerator);
		$sel->addSelectedProperty ('user_id');
		$sel->addFilter ('blog_id', $blogId);
		//$sel->addFilter ('user_id', ExplaySession::getUserId (), '!=');
		
		$res = $sel->getList ();
		
		$aMembersObjects = array ();
		$aAdmins = array ();
		$aModerators = array ();
		
		foreach ($res as $row) {
			if ($row[1] == $oTypeMember->getId ()) {
				$aMembersObjects[] = $objectsController->getObject ($typesController->getTypeById ($row[1]), $row[0]);
			} else if ($row[1] == $oTypeAdmin->getId ()) {
				$aAdmins[$row['user_id']] = true;
			} else {
				$aModerators[$row['user_id']] = true;
			}
		}
		
		$response = new ModuleResponse;
		$response->setTemplate ('blogs/forms/blogMembers');
		$response->setAttribute ('blog-id', $blogId);
		
		foreach ($aMembersObjects as $object) {
			$object->setAttribute ('admin', isset ($aAdmins[$object->getValue('user_id')]) ? '1' : '0');
			$object->setAttribute ('moderator', isset ($aModerators[$object->getValue('user_id')]) ? '1' : '0');
			
			$response->addObject ($object);
		}
		
		return $response;
	}
	
	/*public function rss () {
		$typesController = TypesController::getInstance ();
		
		$requestWhat = getRequest ('param0');
		
		if ($requestWhat == '') {
			throw new NotFound;
		}
		
		$aFilters = array ('draft' => 0);
		$aTypes = null;
		$aPages = array ('page' => 0, 'onpage' => 15);
		$aAttributes = array (
			'blog-id' => 0,
			'blog-name' => '',
			'user-login' => '',
			'user-id' => 0
		);
		
		if ($requestWhat == 'blog') {
			$blogName = getRequest ('param1');
			if ($blogName == '') {
				throw new NotFound;
			}
			
			$oTypeBlog = TypesController::getInstance()->getTypeByName ('blog');
			
			$sel = new Selection;
			$sel->addType ($oTypeBlog);
			$sel->addFilter ('alias', $blogName);
			$sel->addSelectedProperty ('title');
			$sel->addLimit (1);
			
			$blog = $sel->getList ();
			
			if (count ($blog) == 0) {
				throw new NotFound;
			}
			
			$aFilters['blog_id'] = $blog[0][0];
			$aAttributes['blog-id'] = $blog[0][0];
			$aAttributes['blog-name'] = $blogName;
			$this->setPageTitle (lang ('header_rss_blog',__CLASS__) . $blog[0]['title']);
		} elseif ($requestWhat == 'user') {
			$userLogin = getRequest ('param1');
			if ($userLogin == '') {
				throw new NotFound;
			}
			
			$oUserType = TypesController::getInstance()->getTypeByName ('user');
			
			$sel = new Selection;
			$sel->addType ($oUserType);
			$sel->addFilter ('login', $userLogin);
			$sel->addSelectedProperty ('name');
			$sel->addLimit (1);
			
			$res = $sel->getList ();
			
			if (count ($res) == 0) {
				throw new NotFound;
			}
			
			$aFilters['owner_id'] = $res[0][0];
			$aAttributes['user-id'] = $res[0][0];
			$aAttributes['user-login'] = $userLogin;
			$this->setPageTitle (lang ('header_rss_user',__CLASS__) . $res[0]['name']);
		} else {
			switch ($requestWhat) {
				case 'all' : {
					$this->setPageTitle (lang ('header_rss_all',__CLASS__));
					break;
				}
				case 'thematic' : {
					$this->setPageTitle (lang ('header_rss_thematic',__CLASS__));
					$aFilters['blog_id'] = array (0, '!=');
					break;
				}
				case 'personal' : {
					$this->setPageTitle (lang ('header_rss_personal',__CLASS__));
					$aFilters['blog_id'] = 0;
					break;
				}
				default : {
					$this->setPageTitle (lang ('header_rss_popular',__CLASS__));
					$aFilters['rating'] = array ((int) Regedit::getInstance()->get('//modules/blogs/rating_to_mainpage'), '>=');
				}
			}
		}
		
		return $this->getPostsList($aTypes, $aFilters, $aPages, $aAttributes);
	}*/
	
	/**
	 * Получить объект блога
	 * @param integer $id id блога
	 * @return ModuleResponse
	 */
	public function getBlog ($id) {
		$response = new ModuleResponse;
		
		if ($object = ObjectsController::getInstance()->getObject (TypesController::getInstance()->getTypeByName ('blog'), (int) $id)) {
			$object->setAttribute ('user-member', $this->isUserBlogMember ($id) ? 1 : 0);
			$object->setAttribute ('moderator', $this->isUserModerateBlog ($id) ? 1 : 0);
			$object->setAttribute ('admin', $this->isUserAdminBlog ($id) ? 1 : 0);
			
			if (userGroup (ExplaySession::getUser()->getValue ('group')) == 'guest') {
				$object->setAttribute ('vote-allow', '0');
			} else {
				$object->setAttribute ('vote-allow', '1');
			}
			
			$response->addObject ($object); 
		}
		
		return $response;
	}

	/**
	 * Список (<select>) блогов, в которые может писать пользователь
	 * @param integer $slectedId id выбранного из списка блогов (по-умолчанию 0)
	 * @return unknown
	 */
	public function getUserBlogsSelection ($slectedId = 0) {
		$objectsController = ObjectsController::getInstance();
		$oType = TypesController::getInstance()->getTypeByName ('blog_member');
		
		$sel = new Selection;
		$sel->addType ($oType);
		$sel->addFilter ('user_id', ExplaySession::getUserId ());
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
		
		$response = new ModuleResponse;
		
		foreach ($aObjects as $object) {
			$object->setAttribute ('selected', $slectedId == $object->getValue ('blog_id') ? '1' : '0');
			$object->setAttribute ('closed', $object->getValue ('blog_closed') ? '1' : '0');
			$response->addObject ($object);
		}
		
		return $response;
	}
	
	/**
	 * Облако тегов
	 * @return ModuleResponse
	 */
	public function getTagsCloud () {
		
		$typesController = TypesController::getInstance ();
		
		$aTypes = array ();
		
		// Добавляем нужные типы
		if (self::$aPostsTypes === null) {
			self::loadPostsTypes ();
		}
		
		foreach (self::$aPostsTypes as $typeId) {		
			$aTypes[] = $typesController->getTypeById ($typeId);
		}
		
		$aTags = FDump::getCountObjectValues ($aTypes, 'tags');
		
		$k = 40 / (log ($aTags['max'] - $aTags['min'] + 1.1));
		
		$res = array ();
		
		for ($i = 0; $i < 50; ++$i) {
			if (!isset ($aTags['values'][$i])) {
				break;
			}
			
			$size = log ($aTags['values'][$i]['cnt'] + 1 - $aTags['min']) * $k;
			$size = $size > 10 ? $size : 10;
			$size = $size < 40 ? $size : 40;
			
			$res[]['tag'] = array (
				'value' => $aTags['values'][$i]['value'],
				'cnt'   => $aTags['values'][$i]['cnt'],
				'size'  => $size
			);
		}
		
		$response = new ModuleResponse;
		$response->addObject (new Array2XML ('tags', $res));
		
		return $response;		
	}
	
	/**
	 * Блок рейтинга блогов
	 * @return ModuleResponse
	 */
	public function getBlogsBlock () {
		$oTypeBlog = TypesController::getInstance()->getTypeByName ('blog');
		$objectsController = ObjectsController::getInstance ();
		
		$sel = new Selection;
		$sel->addType ($oTypeBlog);
		$sel->addLimit ((int) Regedit::getInstance()->get ('//modules/blogs/blogs_in_block'));
		$sel->setOrder ('rating', false);
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
		
		$response = new ModuleResponse;
		
		foreach ($aObjects as $object) {
			$response->addObject ($object);
		}
		
		return $response;
	}

	/**
	 * Получить блоги, в котопые вступил пользователь, а так же где он модератор/администратор
	 * @param integer $userId id пользователя
	 * @param string $lawLevel установка этого параметра в "moderator" или "admin" выведет, в каких блогах пользователь модератор/администратор
	 * @return ModuleResponse
	 */
	public function getJoinBlogsList ($userId, $lawLevel = false) {
		$objectsController = ObjectsController::getInstance ();
		$typesController = TypesController::getInstance ();
		
		$sel = new Selection;
		
		switch ($lawLevel) {
			case 'moderator' : {
				$sel->addType ($typesController->getTypeByName ('blog_moderator'));
				break;
			}
			case 'admin' : {
				$sel->addType ($typesController->getTypeByName ('blog_admin'));
				break;
			}
			default : {
				$sel->addType ($typesController->getTypeByName ('blog_member'));
			}
		}
		
		$sel->addFilter ('user_id', (int) $userId);
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
		
		$response = new ModuleResponse;
		
		foreach ($aObjects as $object) {
			$response->addObject ($object);
		}
		
		return $response;
	}
	
	/**
	 * Похожие публикации
	 * @param integer $postId id публикации
	 * @param integer $postTypeId id типа публикации
	 * @param integer $limit количество выводимых ссылок (по-умолчанию 10)
	 * @param string $template используемый шаблон макроса (по-умолчанию blogs/blockSimilarPosts)
	 * @return ModuleResponse
	 */
	public function getSimilarPosts ($postId, $postTypeId, $limit = 10) {
		$typesController = TypesController::getInstance ();
		$objectsController = ObjectsController::getInstance ();
		
		if (!$this->validateType ($postTypeId)) {
			throw new CoreException (lang ('error_type_mismatch',__CLASS__));
		}
		
		$oType = $typesController->getTypeById ($postTypeId);
		
		if (!$oPost = $objectsController->getObject ($oType, $postId)) {
			throw new CoreException (lang('post_not_found',__CLASS__));
		}
		
		$sel = new Selection;
		
		// Добавляем нужные типы
		if (self::$aPostsTypes === null) {
			self::loadPostsTypes ();
		}
		
		foreach (self::$aPostsTypes as $typeId) {		
			$sel->addType ($typesController->getTypeById ($typeId));
		}
		
		$sel->addFilterIn('tags', $oPost->getValue ('tags'));
		$sel->addFilter ('draft', '0');
		$sel->addLimit ($limit);
		$sel->setOrder ('pubdate', false);
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
				
		$response = new ModuleResponse;
		
		$n = 0;
		
		foreach ($aObjects as $object) {
			if ($object->getId () == $postId && $object->getType()->getId () == $postTypeId) {
				continue;
			}
			$response->addObject ($object);
		}
		
		return $response;
	}
	
	/**
	 * Получить список адсинистраторов блога
	 * @param integer $blogId
	 * @return ModuleResponse
	 */
	public function getBlogAdmins ($blogId) {
		$oTypeBlogAdmin = TypesController::getInstance()->getTypeByName ('blog_admin');
		$objectsController = ObjectsController::getInstance ();
		
		$sel = new Selection;
		$sel->addType ($oTypeBlogAdmin);
		$sel->addFilter ('blog_id', $blogId);
		
		$aObjects = $objectsController->getObjectsList($sel->getList ());
		
		$response = new ModuleResponse;
		
		foreach ($aObjects as $object) {
			$response->addObject ($object);
		}
		
		return $response;
	}
	
	/**
	 * @param integer $typeId
	 * @return boolean
	 */
	public static function validateType ($typeId) {
		if (self::$aPostsTypes === null) {
			self::loadPostsTypes ();
		}
		
		return in_array ($typeId, self::$aPostsTypes) ? true : false;
	}
	
/**
	 * Список публикаций
	 * @return ModuleResponse
	 */
	private function getPostsList (array $aTypes = null, array $aCustomFilters = null, array $aPages = null, array $aResponseAttributes = null, $sOrder = 'pubdate', $ascOrder = false) {
		$typesController = TypesController::getInstance ();
		$objectsController = ObjectsController::getInstance ();
		
		$sel = new Selection;
		
		// Добавляем типы
		if ($aTypes === null) {
			if (self::$aPostsTypes === null) {
				self::loadPostsTypes ();
			}
			
			foreach (self::$aPostsTypes as $typeId) {		
				$sel->addType ($typesController->getTypeById ($typeId));
			}
		} else {
			foreach ($aTypes as $oType) {
				$sel->addType ($oType);
			}
		}
		
		// Добавляем фильтры
		if ($aCustomFilters !== null) {
			foreach ($aCustomFilters as $fieldName => $filteredValue) {
				if (is_array ($filteredValue)) {
					$sel->addFilter ($fieldName, $filteredValue[0], $filteredValue[1]);
				} else {
					$sel->addFilter ($fieldName, $filteredValue);
				}
			}
		}
		
		// Сортировка
		$sel->setOrder ($sOrder, $ascOrder);
		
		// Страницы
		if ($aPages === null) {
			$aPages = array ();
			$aPages['page'] = getRequest ('page', 'int');
			if ($aPages['page'] > 0) {
				$aPages['page']--;
			}
			$aPages['onpage'] = (int) Regedit::getInstance()->get ('//modules/blogs/posts_on_page');
		}
		$sel->addLimit ($aPages['page'] * $aPages['onpage'], $aPages['onpage']);
		
		$response = new ModuleResponse;
		
		// Атрибуты ответа
		if ($aResponseAttributes !== null) {
			foreach ($aResponseAttributes as $attrName => $attrValue) {
				$response->setAttribute ($attrName, $attrValue);
			}
		}
			
		$aObjects = $objectsController->getObjectsList ($sel->getList ());
		
		foreach ($aObjects as $object) {
			if ($object) {
				if ($text = $object->getValue ('text')) {
					$text = preg_replace ('/^(.*)<cut[^>]*\/?>(.*)/ius', '\\1<a href="' . HTTP_HOST .'/post/' . $object->getTypeName() . '/' . $object->getId() . '#cut">' . lang('labelReadMore',__CLASS__) . '</a>', $text);
					$object->setValue ('text', $text);
				}
				
				$object->setAttribute ('editable', $this->isPostEditable ($object->getValue ('owner_id'), $object->getValue ('blog_id')) ? '1' : '0');
				if (userGroup (ExplaySession::getUser()->getValue ('group')) != 'guest' && ExplaySession::getUserId () != $object->getValue ('owner_id')) {
					$object->setAttribute ('vote-allow', '1');
				} else {
					$object->setAttribute ('vote-allow', '0');
				}
				
				$response->addObject ($object);
			}
		}
		
		$response->addObject (new Array2XML ('pages', array (
			'onpage' => $aPages['onpage'],
			'total' => $sel->getTotalCount (),
			'page' => ++$aPages['page']
		)));
		
		//$response->setAttribute ('vote-allow', userGroup (ExplaySession::getUser()->getValue ('group')) != 'guest' ? '1' : '0');
		$response->setTemplate ('blogs/postsList');
		
		return $response;
	}
	
	/**
	 * Форма редактирования блога
	 * @param integer $blogId id блога
	 * @return ModuleResponse
	 */
	private function blogEditForm ($blogId = 'new') {
		$oBlogType = TypesController::getInstance()->getTypeByName ('blog');
		
		$form = new Form ($oBlogType, $blogId);
		$form->buildFromType ();
		
		if ($blogId != 'new') {
			if (!$object = ObjectsController::getInstance()->getObject ($oBlogType, $blogId)) {
				throw new NotFound;
			}
			// 403
			if (!$this->isUserAdminBlog($blogId)) {
				throw new AccessException (lang('error_edit_blog_not_admin',__CLASS__));
			}
			$form->addObjectValues ($object);
		}
		
		if (isset ($this->aCurrentErrors['blog'])) {
			$form->setErrors ($this->aCurrentErrors['blog']);
		}
		if (isset ($this->aCurrentValues['blog'])) {
			$form->setValues ($this->aCurrentValues['blog']);
		}	
		
		$response = new ModuleResponse;
		$response->addObject ($form);
		$response->setTemplate ('blogs/forms/blog');
		return $response;
	}
	
	/**
	 * Сохраняет блог и перенаправляет в него пользователя
	 * @return void
	 */
	private function saveBlog () {
		$typesController = TypesController::getInstance();
		$objectsController = ObjectsController::getInstance();
		
		$oBlogType = $typesController->getTypeByName ('blog');
		
		$id = getRequest ('param0', 'int');
		if ($id == 0) {
			$id = 'new';
		}
		
		$properties = $this->getRequestData ($id, $oBlogType);
		
		if ($id == 'new') {
			$object = new Object ($oBlogType);
			$object->setValues ($properties);
			$object->setValue ('count_members', 1);
			$object->setValue ('rating', 0);
		} else {
			if (!$object = $objectsController->getObject ($oBlogType, $id)) {
				throw new NotFound;
			}
			// 403
			if (!$this->isUserAdminBlog($id)) {
				throw new AccessException (lang('error_edit_blog_not_admin',__CLASS__));
			}
			$object->setValues ($properties);
		}
		
		$errors = $this->getValidateErrors ($object, $oBlogType);
		
		$sel = new Selection;
		$sel->addType ($oBlogType);
		$sel->addFilter ('alias', $object->getValue ('alias'));
		if ($id != 'new') {
			$sel->addFilter ('id', $id, '!=');
			
			
		}
		if ($sel->getTotalCount () > 0) {
			$errors['alias'] = lang ('error_such_blog_alias_exists', __CLASS__);
		}
		
		if (count ($errors)) {
			$this->aCurrentValues['blog'] = $properties;
			$this->aCurrentErrors['blog'] = $errors;			
			return false;
		}
		
		if ($id == 'new') {
			$object->createNew ();
			
			$oMember = new Object ($typesController->getTypeByName ('blog_member'));
			$oMember->setValues (array (
				'blog_id' => $object->getId (),
				'user_id' => ExplaySession::getUserId ()
			));
			$oMember->createNew ();
			
			$oAdmin = new Object ($typesController->getTypeByName ('blog_admin'));
			$oAdmin->setValues (array (
				'blog_id' => $object->getId (),
				'user_id' => ExplaySession::getUserId ()
			));
			$oAdmin->createNew ();
		} else {
			$object->save ();
		}
		
		redirect (HTTP_HOST . '/blog/' . $object->getValue('alias') .'/');
	}
	
	/**
	 * Форма редактирования публикации
	 * @param string $sTypeName имя типа публикации
	 * @param integer $objectId id объекта (по-умолчанию new)
	 * @return ModuleResponse
	 */
	private function postEditForm ($sTypeName, $objectId = 'new') {
		if (!$oType = TypesController::getInstance()->getTypeByName ($sTypeName)) {
			throw new NotFound;
		}
		if (!self::validateType ($oType->getId ())) {
			throw new NotFound;
		}
		
		$objectId = $objectId == 'new' ? 'new' : (int) $objectId;
		
		$form = new Form ($oType, $objectId);
		$form->buildFromType ();
		
		if ($objectId == 0 && $objectId != 'new') {
			throw new NotFound;
		}
		if ($objectId != 'new') {
			if (!$object = ObjectsController::getInstance()->getObject ($oType, $objectId)) {
				throw new NotFound;
			}
			// 403
			if (!$this->isPostEditable ($object->getValue ('owner_id'), $object->getValue ('blog_id'))) {
				throw new AccessException (lang('error_edit_post_not_author',__CLASS__));
			}
			$form->addObjectValues ($object);
		}
		
		if (isset ($this->aCurrentErrors[$sTypeName])) {
			$form->setErrors ($this->aCurrentErrors[$sTypeName]);
		}
		if (isset ($this->aCurrentValues[$sTypeName])) {
			$form->setValues ($this->aCurrentValues[$sTypeName]);
		}
			
		$response = new ModuleResponse;
		$response->addObject ($form);
		$response->setTemplate ('blogs/forms/post');
		$response->setAttribute ('type', $oType->getName ());
		return $response;
	}
	
	/**
	 * Сохраняет публикацию и перенаправляет на него пользователя
	 * @return void
	 */
	private function savePost () {
		$typesController = TypesController::getInstance();
		$objectsController = ObjectsController::getInstance();
		
		// Проверяем тип публикации
		$requestType = getRequest ('param0');
		if (mb_strlen ($requestType) == 0) {
			throw new NotFound;
		}
		
		if (!$oType = $typesController->getTypeByName ($requestType)) {
			throw new NotFound;
		}
		
		if (!self::validateType ($oType->getId ())) {
			throw new NotFound;
		}
		
		$id = getRequest ('param1', 'int');
		if ($id == 0) {
			$id = 'new';
		}
		
		$properties = $this->getRequestData ($id, $oType);
		$bWasPublished = false;
		$bNeedLog = false;
		
		// Создаем сам объект или выбираем из БД
		if ($id == 'new') {
			$object = new Object ($oType);
			$object->setValues ($properties);
			$object->setValue ('pubdate', time ());
			$object->setValue ('owner_id', ExplaySession::getUserId ());
			$object->setValue ('published', $object->getValue ('draft') == '0' ? '1' : '0');
		} else {
			if (!$object = $objectsController->getObject ($oType, $id)) {
				throw new NotFound;
			}
			// 403
			if (!$this->isPostEditable ($object->getValue ('owner_id'), $object->getValue ('blog_id'))) {
				throw new AccessException (lang('error_edit_post_not_author',__CLASS__));
			}
			$oldBlogId = $object->getValue ('blog_id');
			$object->setValues ($properties);
			
			$bWasPublished = $object->getValue ('published') == '1' ? true : false;
		}
		
		// Проверяем на ошибки заполнения формы
		$errors = $this->getValidateErrors ($object, $oType);
		
		if ($object->getValue ('blog_id') != 0 && !$oBlog = $objectsController->getObject ($typesController->getTypeByName ('blog'), $object->getValue ('blog_id'))) {
			$errors['blog_id'] = lang ('error_post_saving_wrong_blog', __CLASS__); 
		}
		
		if (!empty ($oBlog) && $oBlog->getValue ('closed')) {
			if ($id == 'new' || $object->getValue ('blog_id') != $oldBlogId) {
				$errors['blog_id'] = lang ('error_post_saving_blog_closed', __CLASS__);
			} 
		}
		
		if (count ($errors)) {
			$this->aCurrentValues[$requestType] = $properties;
			$this->aCurrentErrors[$requestType] = $errors;
			return false;
		}
		
		// Ставим корректную дату публикации (для черновиков)
		if ($object->getValue ('draft') == '0' && !$bWasPublished) {
			$object->setValue ('pubdate', time ());
			$object->setValue ('published', '1');
			$bNeedLog = true;
		}
		
		// Добавляем/обновляем объект
		if ($id == 'new') {
			if ($object->getValue ('blog_id') != 0) {
				$blogPosts = $oBlog->getValue ('count_posts');
				$oBlog->setValue ('count_posts', ++$blogPosts);
				$oBlog->save ();
			} 
			
			$object->createNew ();
		} else {
			$object->save ();
			
		}

		// Добавляем лог
		if ($bNeedLog) {
			$this->addPostLog ($object);
		}
		
		redirect (HTTP_HOST . '/post/' . $requestType .'/' . $object->getId ());
	}
	
	/**
	 * Добавляет лог-запись о новой публикации
	 * @param Object $oPost объект публикации
	 */
	private function addPostLog (Object $oPost) {
		if (Regedit::getInstance()->get ('//settings/site/use_logs') == 1) {
			$typesController = TypesController::getInstance ();
			
			$selLogType = new Selection;
			$selLogType->addType ($typesController->getTypeByName ('post_log_type'));
			$selLogType->addFilter ('parent_type_id', $oPost->getType()->getId ());
			$selLogType->addSelectedProperty ('log_type_id');
			$selLogType->addLimit (1);
			$aLogType = $selLogType->getList ();
			
			if (count ($aLogType)) {
				$oLog = new Object ($typesController->getTypeById ($aLogType[0]['log_type_id']));
				$oLog->setValues (array (
					'date' => time (),
					'object_id' => $oPost->getId (),
					'user_id' => ExplaySession::getUserId ()
				));
				$oLog->createNew ();
			}
		}
	}
	
	/**
	 * @return void
	 */
	private static function loadPostsTypes () {
		$sel = new Selection;
		$sel->addType (TypesController::getInstance()->getTypeByName ('post_type'));
		$sel->addSelectedProperty ('type_id');
		
		$res = $sel->getList ();
		
		foreach ($res as $item) {
			self::$aPostsTypes[] = $item['type_id'];
		}
	}

	/**
	 * Загружает блоги, в которые вступил пользователь
	 * @return void
	 */
	private function loadUserBlogs () {
		$sel = new Selection;
		$sel->addType (TypesController::getInstance()->getTypeByName ('blog_member'));
		$sel->addFilter ('user_id', ExplaySession::getUserId ());
		$sel->addSelectedProperty ('blog_id');
		
		$res = $sel->getList ();
		
		$this->aUserBlogs = array ();
		foreach ($res as $item) {
			$this->aUserBlogs[] = $item['blog_id'];
		}
	}
	
	/**
	 * Загрузить модерируемые пользователем блоги
	 */
	private function loadUserModerateBlogs () {
		if (userGroup (ExplaySession::getUser()->getValue ('group')) == 'guest') {
			$this->aUserModerateBlogs = array ();
			return;
		}
		
		$sel = new Selection;
		$sel->addType (TypesController::getInstance()->getTypeByName ('blog_moderator'));
		$sel->addFilter ('user_id', ExplaySession::getUserId ());
		$sel->addSelectedProperty ('blog_id');
		
		$res = $sel->getList ();
		
		$this->aUserModerateBlogs = array ();
		foreach ($res as $item) {
			$this->aUserModerateBlogs[] = $item['blog_id'];
		}
	}
	
	/**
	 * Загрузить администрируемые пользователем блоги
	 */
	private function loadUserAdminBlogs () {
		if (userGroup (ExplaySession::getUser()->getValue ('group')) == 'guest') {
			$this->aUserAdminBlogs = array ();
			return;
		}
		
		$sel = new Selection;
		$sel->addType (TypesController::getInstance()->getTypeByName ('blog_admin'));
		$sel->addFilter ('user_id', ExplaySession::getUserId ());
		$sel->addSelectedProperty ('blog_id');
		
		$res = $sel->getList ();
		
		$this->aUserAdminBlogs = array ();
		foreach ($res as $item) {
			$this->aUserAdminBlogs[] = $item['blog_id'];
		}
	}
	
	/**
	 * Проверка на доступ пользователя к редактированию публикации
	 * @param integer $postOwnerId id автора публикации
	 * @param integer $blogId id блога
	 * @return boolean
	 */
	private function isPostEditable ($postOwnerId, $blogId) {
		if ($postOwnerId == ExplaySession::getUserId () || userGroup (ExplaySession::getUser()->getValue ('group')) == 'admin') {
			return true;
		}
		
		if ($this->aUserModerateBlogs == null) {
			$this->loadUserModerateBlogs ();
		}
		if (in_array ($blogId, $this->aUserModerateBlogs)) {
			return true;
		}
		
		if ($this->aUserAdminBlogs == null) {
			$this->loadUserAdminBlogs ();
		}
		if (in_array ($blogId, $this->aUserAdminBlogs)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * Проверка на возможность пользователя модерировать блог
	 * @param integer $blogId id блога
	 */
	private function isUserModerateBlog ($blogId) {
		if (userGroup (ExplaySession::getUser()->getValue ('group')) == 'admin') {
			return true;
		}
		
		if ($this->aUserModerateBlogs == null) {
			$this->loadUserModerateBlogs ();
		}
		if (in_array ($blogId, $this->aUserModerateBlogs)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * Проверка на возможность пользователя администрировать блог
	 * @param integer $blogId id блога
	 */
	private function isUserAdminBlog ($blogId) {
		if (userGroup (ExplaySession::getUser()->getValue ('group')) == 'admin') {
			return true;
		}
		
		if ($this->aUserAdminBlogs == null) {
			$this->loadUserAdminBlogs ();
		}
		if (in_array ($blogId, $this->aUserAdminBlogs)) {
			return true;
		}
		
		return false;
	}
	
	/**
	 * @param integer $blogId
	 * @return boolean
	 */
	private function isUserBlogMember ($blogId) {
		if ($this->aUserBlogs === null) {
			$this->loadUserBlogs ();
		}

		return in_array ($blogId, $this->aUserBlogs) ? true : false;
	}
	
}